package cn.zswltech.gruul.common.util;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * io密集型线程池-线程隔离术
 *
 * @author xuluquan
 * @version 1.0
 * @date 2018-08-06 11:45
 **/
public class ThreadPoolUtils {

    /**
     * 分为cpu密集型和io密集型线程池，io密集型公式如下
     * 线程池核心线程数,cpu/(1-w/c)
     */
    private static int CORE_POOL_SIZE = 20;

    /**
     * 线程池最大线程数
     */
    private static int MAX_POOL_SIZE = 80;

    /**
     * 额外线程空状态生存时间
     */
    private static int KEEP_ALIVE_TIME = 10;

    /**
     * 阻塞队列。当核心线程都被占用，且阻塞队列已满的情况下，才会开启额外线程。
     */
    private static BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>(Integer.MAX_VALUE / 2000);

    /**
     * 线程池对拒绝任务的处理策略
     **/
    private static RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

    /**
     * 线程池
     */
    public final static ThreadPoolExecutor threadPool;

    private ThreadPoolUtils() {
    }

    /**
     * 线程工厂
     **/
    private static ThreadFactory threadFactory = new ThreadFactory() {
        private final AtomicLong atomicLong = new AtomicLong();

        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "bifrost thread:" + atomicLong.getAndIncrement());
        }
    };

    static {
        threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, workQueue,
                threadFactory, handler);
    }

    /**
     * Execute.
     *
     * @param runnable the runnable
     */
    public static void execute(Runnable runnable) {
        threadPool.execute(runnable);
    }

    /**
     * Execute.
     *
     * @param futureTask the future task
     */
    public static void execute(FutureTask futureTask) {
        threadPool.execute(futureTask);
    }

    /**
     * Parallel.
     *
     * @param futureTasks the future tasks
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException   the execution exception
     */
    public static void parallel(List<Callable<Void>> futureTasks) throws InterruptedException, ExecutionException {
        List<Future<Void>> futures = threadPool.invokeAll(futureTasks);
        for (Future<Void> future : futures) {
            future.get();
        }
    }

    /**
     * Parallel.
     *
     * @param futureTasks    the future tasks
     * @param timeoutSeconds the timeout second
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException   the execution exception
     * @throws TimeoutException     the timeout exception
     */
    public static void parallel(List<Callable<Void>> futureTasks, long timeoutSeconds)
            throws InterruptedException, ExecutionException, TimeoutException {
        parallel(futureTasks, timeoutSeconds, TimeUnit.SECONDS);
    }

    /**
     * Parallel.
     *
     * @param futureTasks the future tasks
     * @param timeout     the timeout
     * @param unit        the unit
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException   the execution exception
     * @throws TimeoutException     the timeout exception
     */
    public static void parallel(List<Callable<Void>> futureTasks, long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
        List<Future<Void>> futures = threadPool.invokeAll(futureTasks);
        for (Future<Void> future : futures) {
            future.get(timeout, unit);
        }
    }

    /**
     * Parallel for list list.
     *
     * @param <T>         the type parameter
     * @param futureTasks the future tasks
     * @return the list
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException   the execution exception
     */
    public static <T> List<T> parallelForList(List<Callable<T>> futureTasks)
            throws InterruptedException, ExecutionException {
        List<Future<T>> futures = threadPool.invokeAll(futureTasks);
        List<T> results = new ArrayList<>();
        for (Future<T> future : futures) {
            results.add(future.get());
        }
        return results;
    }

    /**
     * Parallel for list list.
     *
     * @param <T>            the type parameter
     * @param futureTasks    the future tasks
     * @param timeoutSeconds the timeout seconds
     * @return the list
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException   the execution exception
     * @throws TimeoutException     the timeout exception
     */
    public static <T> List<T> parallelForList(List<Callable<T>> futureTasks, long timeoutSeconds)
            throws InterruptedException, ExecutionException, TimeoutException {
        return parallelForList(futureTasks, timeoutSeconds, TimeUnit.SECONDS);
    }

    /**
     * Parallel for list list.
     *
     * @param <T>         the type parameter
     * @param futureTasks the future tasks
     * @param timeout     the timeout
     * @param unit        the unit
     * @return the list
     * @throws InterruptedException the interrupted exception
     * @throws ExecutionException   the execution exception
     * @throws TimeoutException     the timeout exception
     */
    public static <T> List<T> parallelForList(List<Callable<T>> futureTasks, long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
        List<Future<T>> futures = threadPool.invokeAll(futureTasks);
        List<T> results = new ArrayList<>();
        for (Future<T> future : futures) {
            results.add(future.get(timeout, unit));
        }
        return results;
    }


}
